package com.mask.common.mybatis.interceptor;

import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.mask.common.core.model.LoginUser;
import com.mask.common.core.utils.SecurityUtils;
import com.mask.common.mybatis.annotation.DataScope;
import com.mask.common.mybatis.enums.DataScopeTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.lang.reflect.Method;
import java.sql.Connection;

@Slf4j
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataScopeInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        
        // 获取SQL
        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        String sql = boundSql.getSql();
        
        // 获取权限注解
        DataScope dataScope = getDataScopeAnnotation();
        if (dataScope == null) {
            return invocation.proceed();
        }
        
        // 获取当前用户的数据权限
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (loginUser == null) {
            return invocation.proceed();
        }
        
        // 优先获取注解中的权限类型
        DataScopeTypeEnum dataScopeType = dataScope.type();
        if (dataScopeType == DataScopeTypeEnum.ALL) {
            return invocation.proceed();
        }
        
        // 构建数据权限SQL
        String dataScopeSql = buildDataScopeSql(dataScope, loginUser);
        
        // 重写SQL
        String newSql = sql + dataScopeSql;
        metaObject.setValue("delegate.boundSql.sql", newSql);
        
        return invocation.proceed();
    }

    /**
     * 构建数据权限SQL
     */
    private String buildDataScopeSql(DataScope dataScope, LoginUser loginUser) {
        StringBuilder sqlBuilder = new StringBuilder();
        
        String deptAlias = dataScope.deptAlias();
        String userAlias = dataScope.userAlias();
        
        switch (dataScope.type()) {
            case CUSTOM:
                sqlBuilder.append(String.format(
                    " AND %s.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = %s )",
                    deptAlias, loginUser.getDeptId()));
                break;
            case DEPT:
                sqlBuilder.append(String.format(
                    " AND %s.dept_id = %s",
                    deptAlias, loginUser.getDeptId()));
                break;
            case DEPT_AND_CHILD:
                sqlBuilder.append(String.format(
                    " AND %s.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = %s OR find_in_set( %s , ancestors ) )",
                    deptAlias, loginUser.getDeptId(), loginUser.getDeptId()));
                break;
            case SELF:
                sqlBuilder.append(String.format(
                    " AND %s.create_by = '%s'",
                    userAlias, loginUser.getUsername()));
                break;
            default:
                break;
        }
        
        return sqlBuilder.toString();
    }

    /**
     * 获取数据权限注解
     */
    private DataScope getDataScopeAnnotation() {
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        for (StackTraceElement element : stackTrace) {
            try {
                Class<?> clazz = Class.forName(element.getClassName());
                Method[] methods = clazz.getDeclaredMethods();
                for (Method method : methods) {
                    if (method.getName().equals(element.getMethodName())) {
                        return method.getAnnotation(DataScope.class);
                    }
                }
            } catch (ClassNotFoundException e) {
                continue;
            }
        }
        return null;
    }
}